﻿// MyProject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
//公众号：程序员速成 ，内含一辈子都让你感激自己的优质视频教程，欢迎关注

#include <iostream>


#ifdef _DEBUG   //只在Debug（调试）模式下
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定义new运算符
#define new DEBUG_NEW
#endif
#endif

//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996) 

namespace _nmsp1
{	
	//薪水处理类
	class SalaryHandler
	{
	public:
		//处理加薪请求
		void raiseRequest(const string& sname, int salfigure) //参数1代表要加薪的员工名字，参数2代表要加薪多少
		{
			if (salfigure <= 1000)
			{
				//加薪要求不超过1000，部门经理可以批准
				depManagerSP(sname, salfigure);
			}
			else if (salfigure <= 5000)
			{
				//加薪要求在1000元之上但不超过5000元，技术总监才能审批
				CTOSP(sname, salfigure);
			}
			else
			{
				//加薪要求超过5000元，总经理才能审批
				genManagerSP(sname, salfigure);
			}
		}

	private:
		//部门经理审批加薪请求
		void depManagerSP(const string& sname, int salfigure)
		{
			cout << sname << "的加薪要求为：" << salfigure << "元，部门经理审批通过!" << endl;
		}

		//技术总监审批加薪请求
		void CTOSP(const string& sname, int salfigure)
		{
			cout << sname << "的加薪要求为：" << salfigure << "元，技术总监审批通过!" << endl;
		}

		//总经理审批加薪请求
		void genManagerSP(const string& sname, int salfigure)
		{
			cout << sname << "的加薪要求为：" << salfigure << "元，总经理审批通过!" << endl;
		}
	};
}

namespace _nmsp2
{
	//加薪请求类
	class RaiseRequest
	{
	public:
		//构造函数
		RaiseRequest(const string& sname, int salfigure) :m_sname(sname),m_isalfigure(salfigure){}

		//获取请求加薪的人员名字
		const string& getName() const
		{
			return m_sname;
		}
		//获取请求加薪的数字
		int getSalFigure() const
		{
			return m_isalfigure;
		}

	private:
		string m_sname; //请求加薪的人员名字
		int m_isalfigure; //请求加薪的数字
	};

	//薪水审批者父类
	class ParSalApprover
	{
	public:
		ParSalApprover() :m_nextChain(nullptr) {}
		virtual ~ParSalApprover() {} //做父类时析构函数应该为虚函数

		//设置指向的职责链中的下个审批者
		void setNextChain(ParSalApprover* next)
		{
			m_nextChain = next;
		}

		//处理加薪请求
		virtual void processRequest(const RaiseRequest& req) = 0;

	protected:
		//找链中的下个对象并把请求投递给下个链中的对象
		void sendRequestToNextHandler(const RaiseRequest& req)
		{
			//找链中的下个对象
			if (m_nextChain != nullptr)
			{
				//把请求传递给链中的下个对象
				m_nextChain->processRequest(req);
			}
			else
			{
				//没找到链中下个对象，程序流程执行这里似乎不应该
				cout << req.getName() << "的加薪要求为：" << req.getSalFigure() << "元，但无人能够审批！" << endl;
			}
		}
	private:
		ParSalApprover* m_nextChain; //指向下一个审批者（对象）的多态指针（指向自身类型），每个都指向下一个，就会构成一个职责链（链表）
	};

	//部门经理子类
	class depManager_SA :public ParSalApprover
	{
	public:
		//处理加薪请求
		virtual void processRequest(const RaiseRequest& req)
		{
			int salfigure = req.getSalFigure();
			if (salfigure <= 1000)
			{
				//如果自己能处理，则自己处理
				cout << req.getName() << "的加薪要求为：" << salfigure << "元，部门经理审批通过！" << endl;
			}
			else
			{
				//自己不能处理，尝试找链中的下个对象来处理
				sendRequestToNextHandler(req);
			}
		}
	};

	//技术总监子类
	class CTO_SA :public ParSalApprover
	{
	public:
		//处理加薪请求
		virtual void processRequest(const RaiseRequest& req)
		{
			int salfigure = req.getSalFigure();
			if (salfigure > 1000 && salfigure <= 5000)
			{
				//如果自己能处理，则自己处理
				cout << req.getName() << "的加薪要求为：" << salfigure << "元，技术总监审批通过！" << endl;
			}
			else
			{
				//自己不能处理，尝试找链中的下个对象来处理
				sendRequestToNextHandler(req);
			}
		}
	};

	//总经理子类
	class genManager_SA :public ParSalApprover
	{
	public:
		//处理加薪请求
		virtual void processRequest(const RaiseRequest& req)
		{
			int salfigure = req.getSalFigure();
			if (salfigure > 5000)
			{
				//如果自己能处理，则自己处理
				cout << req.getName() << "的加薪要求为：" << salfigure << "元，总经理审批通过！" << endl;
			}
			else
			{
				//自己不能处理，尝试找链中的下个对象来处理
				sendRequestToNextHandler(req);
			}
		}
	};
}

namespace _nmsp3
{
	//敏感词过滤器父类
	class ParWordFilter
	{
	public:
		ParWordFilter() :m_nextChain(nullptr) {}
		virtual ~ParWordFilter() {} //做父类时析构函数为虚函数

		//设置指向的职责链中的下个过滤器
		void setNextChain(ParWordFilter* next)
		{
			m_nextChain = next;
		}

		//处理敏感词过滤请求
		virtual string processRequest(string strWord) = 0;

	protected:
		//找链中的下个对象并把请求投递给下个链中对象
		string sendRequestToNextHandler(string strWord)
		{
			//找链中下个对象
			if (m_nextChain != nullptr)
			{
				//把请求投递给链中的下个对象
				return m_nextChain->processRequest(strWord);
			}
			return strWord;
		}

	private:
		ParWordFilter* m_nextChain;
	};

	//性敏感词过滤器子类
	class SexyWordFilter :public ParWordFilter
	{
	public:
		virtual string processRequest(string strWord)
		{
			cout << "通过与词库比对，在strWord中查找\"性\"敏感词并用XXX来替换!" << endl;
			strWord += "XXX"; //测试代码，具体的实现逻辑略......
			return sendRequestToNextHandler(strWord);
		}
	};
	//脏话过滤器子类
	class DirtyWordFilter :public ParWordFilter
	{
	public:
		virtual string processRequest(string strWord)
		{
			cout << "通过与词库比对，在strWord中查找\"脏话\"敏感词并用YYY来替换!" << endl;
			strWord += "YYY"; //测试代码，具体的实现逻辑略......
			return sendRequestToNextHandler(strWord);
		}
	};

	//政治敏感词过滤器子类
	class PoliticsWordFilter :public ParWordFilter
	{
	public:
		virtual string processRequest(string strWord)
		{
			cout << "通过与词库比对，在strWord中查找\"政治\"敏感词并用ZZZ来替换!" << endl;
			strWord += "ZZZ"; //测试代码，具体的实现逻辑略......
			return sendRequestToNextHandler(strWord);
		}
	};
}

int main()
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口
	
	//第19章 职责链（Chain Of Responsibility）模式
	//也叫做责任链模式，行为型模式。----看起来与链表非常类似。

	//（1）一个关于涨薪审批的范例
	//加薪请求： <= 1000，部门经理审批。  1000 < 加薪请求 <= 5000，技术总监审批。   加薪请求 > 5000，总经理审批。

	//（2）引入职责链（Chain Of Responsibility）模式
	//定义：使多个对象都有机会处理请求，从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链（构成对象链），
	  //并沿着这条链传递该请求，直到有一个对象处理它为止。
	//3种角色：
	//a)Handler(处理者），ParSalApprover类。
	//b)ConcreteHandler（具体处理者），depManager_SA，CTO_SA，genManager_SA类。
	//c)Client（请求者/客户端）.

	//职责链模式的特点：
	//a)一个请求对应多个接收者，但最后只有一个接收者会处理该请求。 请求发送者和接收者是解耦的。
	//b)直线型职责链，可能会看到环形的或者树形结构的职责链。程序运行期间可以动态的添加、修改、删除
	    //职责链上的接收者，使针对请求的处理更具有灵活性。这是职责链模式的重要特色。
	//c)增加新处理者不需要修改原有代码。符合开闭原则。
	//d)如果请求传递到职责链末尾仍没有得到处理，则应该有一个合理的缺省处理方式。
	//e)如果职责链比较长，能够处理该请求的接收者在职责链中比较靠后，则可能导致请求处理的延迟。
	  //若需要非常快的请求处理速度，则要权衡是否使用职责链模式。
	//f)可以分别选择不同的接收者对象创建多条不同的职责链以增加接收者在职责链模式中的复用性。

	//（3）单纯与非单纯的职责链模式
	//单纯的职责链模式。
	//非单纯的职责链模式 --- 功能链（可以被多个处理者来处理），即便一个请求未被任何处理者对象处理，也允许。
	   //一般用于权限的多次多次校验，数据的多重检查和过滤等场合。
	//范例：敏感词过滤器。






	/*
	_nmsp1::SalaryHandler sh;
	sh.raiseRequest("张三", 15000); //张三要求加薪1.5万
	sh.raiseRequest("李四", 3500);  //李四要求加薪3千5
	sh.raiseRequest("王二", 800);   //王二要求加薪8百
	*/

	/*

	//(1)创建出指责链中包含的各个对象（部门经理、技术总监、总经理）
	_nmsp2::ParSalApprover* pzzlinkobj1 = new _nmsp2::depManager_SA();
	_nmsp2::ParSalApprover* pzzlinkobj2 = new _nmsp2::CTO_SA();
	_nmsp2::ParSalApprover* pzzlinkobj3 = new _nmsp2::genManager_SA();

	//(2)将这些对象串在一起构成职责链（链表），现在职责链中pzzlinkobj1排在最前面，pzzlinkobj3排在最后面。
	pzzlinkobj1->setNextChain(pzzlinkobj2);
	pzzlinkobj2->setNextChain(pzzlinkobj3);
	pzzlinkobj3->setNextChain(nullptr); //可以不写此行，因为ParSalApprover构造函数中设置了m_nextChain为nullptr。

	//(3)创建几位员工关于加薪的请求（对象）
	_nmsp2::RaiseRequest emp1Req("张三", 15000); //张三要求加薪1.5万
	_nmsp2::RaiseRequest emp2Req("李四", 3500); //李四要求加薪3500
	_nmsp2::RaiseRequest emp3Req("王二", 800); //王二要求加薪800
	//看看每位员工的加薪请求由职责链中的哪个对象（部门经理，技术总监，总经理）来处理，从职责链中排在最前面的接收者pzzlinkobj1开始。
	pzzlinkobj1->processRequest(emp1Req);
	pzzlinkobj1->processRequest(emp2Req);
	pzzlinkobj1->processRequest(emp3Req);

	//(4)释放资源
	delete pzzlinkobj1;
	delete pzzlinkobj2;
	delete pzzlinkobj3;
	*/

	//(1)创建出职责链中包含的哥哥对象（性敏感词过滤器，脏话词过滤器，政治敏感词过滤器）
	_nmsp3::ParWordFilter* pwflinkobj1 = new _nmsp3::SexyWordFilter();
	_nmsp3::ParWordFilter* pwflinkobj2 = new _nmsp3::DirtyWordFilter();
	_nmsp3::ParWordFilter* pwflinkobj3 = new _nmsp3::PoliticsWordFilter();

	//(2)将这些对象串在一起构成职责链（链表），现在职责链中pwflinkobj1排在最前面，pwflinkobj3排在最后面。
	pwflinkobj1->setNextChain(pwflinkobj2);
	pwflinkobj2->setNextChain(pwflinkobj3);
	pwflinkobj3->setNextChain(nullptr);

	string strWordFilterResult = pwflinkobj1->processRequest("你好，这里是过滤敏感词测试范例"); //从职责链中排在最前面的接收者pwflinkobj1开始，processRequest的参数代表的是聊天内容。
	cout << "对敏感词过滤后结果为：" << strWordFilterResult << endl;

	//(3)释放资源
	delete pwflinkobj1;
	delete pwflinkobj2;
	delete pwflinkobj3;
		

	return 0;
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件

